/*
 * playback.c
 * This file is part of COOG
 *
 * Copyright (C) 2012 - Luke Westby & Jon Gautsch
 *
 * lab10 is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * lab10 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with lab10. If not, see <http://www.gnu.org/licenses/>.
 */

#include "synth.h"

// helper function to convert character input to a useful array index
int get_note(char);

/* -------------------------------------------------------------------------- *
 * process()
 * the backbone of the synthesis engine
 * generate a tone on a buffer at the frequency specified by key. separate tones
 * from waveforms in wave_1 and wave_2 by a factor of diff. push buffer out on
 * context s.
 * - key = character input from user, to be converted by get_note()
 * - wave_1, wave_2 = float arrays containing waveforms selected by user
 * - freqs = double array built to convert a key into its frequency
 * - diff = multiplicative difference between frequency of wave_1 and wave_2
 * - vol = volume
 * - s = PulseAudio context
 * -------------------------------------------------------------------------- */
void process(char key,
			 float *wave_1,
			 float *wave_2,
			 double *freqs,
			 float diff,
			 float vol,
			 pa_simple *s) {
	
	int note, j, i1, i2;
	float alpha_1, alpha_2, d0_1, d0_2, phase;
	int32_t mask;
	double ff;

	// the audio buffer, chunks of audio data to be sent to hardware queue
	float buf[BUFSIZE] = {0};
	
	// get note from keypress
	note = get_note(key);
	if(note < 0) return;
	
	// for binary modulo function
	mask = WAVESIZE - 1;
	
	// phase shift
	ff = (double)(WAVESIZE) / SAMPLERATE * freqs[note];
	
	// restart
	phase = 0;
	
	// loop until key release or other keypress
	while(!gfx_event_waiting()) {
		
		// fill buffer
		for(j = 0; j < BUFSIZE; j++) {

			// linear interpolation calculations
			i1 = (int) phase;
			alpha_1 = phase - (float) i1;
			
			i2 = (int) (phase * diff);
			alpha_2 = (phase * diff) - (float) i2;
			
			d0_1 = wave_1[(i1+1) & mask] - wave_1[i1 & mask];
			d0_2 = wave_2[(i2+1) & mask] - wave_2[i2 & mask];
			
			// place waveform on buffer
			buf[j] = wave_1[i1 & mask] + alpha_1 * d0_1;
			buf[j] += wave_2[i2 & mask] + alpha_2 * d0_2;
			buf[j] *= 0.5 * vol;
			
			// shift phase
			phase += ff;
		}
		
		// write buffer to hardware
		pa_simple_write(s, buf, sizeof(buf), NULL);
		
	}
	
	// tail, to declick the output
	// same algorithm as above
	
	for(j = 0; j < BUFSIZE; j++) {
		i1 = (int) phase;
		alpha_1 = phase - (float) i1;
		i2 = (int) (phase * diff);
		alpha_2 = (phase * diff) - (float) i2;
		d0_1 = wave_1[(i1+1) & mask] - wave_1[i1 & mask];
		d0_2 = wave_2[(i2+1) & mask] - wave_2[i2 & mask];
		buf[j] = wave_1[(int)phase & mask];
		buf[j] += wave_2[(int)(phase * diff) & mask];
		buf[j] *= 0.667 * vol * (BUFSIZE - j)/BUFSIZE;
		phase += ff;
	}
	
	// write tail
	pa_simple_write(s, buf, sizeof(buf), NULL);
}

/* -------------------------------------------------------------------------- *
 * get_note()
 * just a lookup function to get note from key
 * could have used a lookup table as well, but we already wrote this
 * -------------------------------------------------------------------------- */

int get_note(char c) {
	
	int note;
	
	switch (c) {
		case 'q':
            // play c3 (middle c)
			note = 0;
            break;
        case 'w':
            // play c#3
            note = 1;
            break;
        case 'e':
            // play d3
            note = 2;
            break;
        case 'r':
            // play d#3
            note = 3;
            break;
        case 't':
            // play e3
			note = 4;
            break;
        case 'y':
            // play f3
            note = 5;
            break;
        case 'u':
            // play f#3
            note = 6;
            break;
        case 'i':
            // play g3
            note = 7;
            break;
        case 'o':
            // play g#3
            note = 8;
            break;
        case 'p':
            // play a3
            note = 9;
            break;
        case 'a':
            // play a#3
            note = 10;
            break;
        case 's':
            // play b3
           	note = 11;
            break;
        case 'd':
            // play c3
            note = 12;
            break;
        case 'f':
            // play c#3
            note = 13;
            break;
        case 'g':
            // play d3
            note = 14;
            break;
        case 'h':
            // play d#3
            note = 15;
            break;
        case 'j':
            // play e3
            note = 16;
            break;
        case 'k':
            // play f3
            note = 17;
            break;
        case 'l':
            // play f#3
            note = 18;
            break;
        case 'z':
            // play g3
            note = 19;
            break;
        case 'x':
            // play g#3
            note = 20;
            break;
        case 'c':
            // play a3
            note = 21;
            break;
        case 'v':
            // play a#3
            note = 22;
            break;
        case 'b':
            // play b3
            note = 23;
            break;
		default:
			note = -1;
			break;
	}
	
	return note;
}

/* -------------------------------------------------------------------------- *
 * drain()
 * push all audio through the hardware buffer
 * -------------------------------------------------------------------------- */

void drain(pa_simple *s) {

	pa_simple_drain(s, NULL);
}
